package de.lgohlke;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;

import org.slf4j.Logger;

import de.lgohlke.format.UnitFormatter;

public class MemoryPrinter
{
  public enum LOG_LEVEL
  {
    ERROR, WARN, INFO, DEBUG, TRACE;
  }

  private Logger             logger;
  private LOG_LEVEL          logLevel;

  private final MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();

  public MemoryPrinter use(final Logger log)
  {
    this.logger = log;
    return this;
  }

  public MemoryPrinter asLogLevelINFO()
  {
    return asLogLevel(LOG_LEVEL.INFO);
  }

  public MemoryPrinter asLogLevelDEBUG()
  {
    return asLogLevel(LOG_LEVEL.DEBUG);
  }

  private MemoryPrinter asLogLevel(final LOG_LEVEL level)
  {
    this.logLevel = level;
    return this;
  }

  public void print()
  {
    switch (logLevel)
    {
      case TRACE:
        if (logger.isTraceEnabled())
        {
          logger.trace(getMemoryInfoHeap());
          logger.trace(getMemoryInfoNonHeap());
        }
        break;
      case DEBUG:
        if (logger.isDebugEnabled())
        {
          logger.debug(getMemoryInfoHeap());
          logger.debug(getMemoryInfoNonHeap());
          logger.debug(getMemoryInfoTotal());
        }
        break;
      case INFO:
        if (logger.isInfoEnabled())
        {
          logger.info(getMemoryInfoHeap());
          logger.info(getMemoryInfoNonHeap());
          logger.info(getMemoryInfoTotal());
        }
        break;
      case WARN:
        if (logger.isWarnEnabled())
        {
          logger.warn(getMemoryInfoHeap());
          logger.warn(getMemoryInfoNonHeap());
          logger.warn(getMemoryInfoTotal());
        }
        break;
      case ERROR:
        if (logger.isErrorEnabled())
        {
          logger.error(getMemoryInfoHeap());
          logger.error(getMemoryInfoNonHeap());
          logger.error(getMemoryInfoTotal());
        }
        break;
      default:
        System.err.println("log level not configured: " + logLevel);
    }
  }

  private static String s(final double size)
  {
    return UnitFormatter.readableFileSize(size);
  }

  private String getFormattedMemoryInfo(final MemoryUsage mu)
  {

    // Retrieve the four values stored within MemoryUsage:
    // init: Amount of memory in bytes that the JVM initially requests from the OS.
    // used: Amount of memory used.
    // committed: Amount of memory that is committed for the JVM to use.
    // max: Maximum amount of memory that can be used for memory management.
    return String.format("Init: %s, Used: %s, Committed: %s, Max.: %s", s(mu.getInit()), s(mu.getUsed()), s(mu.getCommitted()), s(mu.getMax()));
  }

  private String getMemoryInfoHeap()
  {
    return "MEMORY Heap: " + getFormattedMemoryInfo(memBean.getHeapMemoryUsage());
  }

  private String getMemoryInfoNonHeap()
  {
    return "MEMORY Non-Heap: " + getFormattedMemoryInfo(memBean.getNonHeapMemoryUsage());
  }

  private String getMemoryInfoTotal()
  {
    Runtime rt = Runtime.getRuntime();
    MemoryUsage memoryUsage = new MemoryUsage(-1, rt.totalMemory(), rt.totalMemory(), rt.maxMemory());
    return "MEMORY runtime: " + getFormattedMemoryInfo(memoryUsage);
  }
}
